/*! nmatc.c
 * \file
 * \author Marco Luisi
 * \brief Server per il sistema NMAT
 *  Created on: Jan 9, 2010
 *  Author: Marco Luisi
 */

#include "nmatc.h"

char *buffer;
char *login_msg;
char *pid;
char *pipe_in;
char *pipe_out;
char *to_server;
char *username;


//membri struct messaggio

char *parse_buffer = NULL; //vedere quando nullificarlo
char **tkns;

message_t req = {0};
message_t res;

int aux;

int con_check;
int in;
int nmat_pipe;
int out;

char input_buffer[MAX_CANON];

int tkns_num = 0;//reso globale
int cmd_code = -1;

/**
 *  nmatc fornisce una interfaccia testuale per le operazioni
 *  gestite dal server nmats.
 */

int main(int argc, char **argv) {

	int cmd;
	int go;
	int ack; //cam
	
	struct sigaction pipe_action;
	bzero(&pipe_action, sizeof(pipe_action));
	pipe_action.sa_handler = pipe_handler;
	sigaction(SIGPIPE, &pipe_action, NULL);

	struct sigaction term_action;
	bzero(&term_action, sizeof(term_action));
	term_action.sa_handler = term_handler;
	sigaction(SIGTERM, &term_action, NULL);

	struct sigaction int_action;
	bzero(&int_action, sizeof(int_action));
	int_action.sa_handler = int_handler;
	sigaction(SIGINT, &int_action, NULL);

	if (argc != 2) {
		WRITELN("NMAT Client: Errore! Fornire il nome utente!");
		WRITELN("NMAT Client: Digitare 'nmat username'");
		WRITELN("NMAT Client in uscita!");
		exit(EXIT_FAILURE);
	}
	username = argv[1];

	init();

	WRITE("Utente ");
	WRITE(username);
	WRITELN(" benvenuto nel sistema NMAT!");

	go = 1;

	while (go) {

		menu();
		//parsing dell'input da tastiera
		//e setup della struct messaggio
		cmd = parse(&req);
		printf("DEBUG:parse cmd = %d\n", cmd);
		switch (cmd) {

		case -1: { //non prende la macro!
			printf("Comando errato\n"); //ridondante?
			break;
		}

		case 11:{ //non prende la macro!
			help();
			break;
		}

		default: {
			msgprint(&req);
			printf("DEbug: main, chiama send\n");
			ack = send(out, &req);
			printf("send ack = %d\n", ack);
			perror("SEND");
		}

		}//fine switch cmd

		printf("In attesa di risposta dal server..\n");

		response(cmd);

		//ack = receive(in,&res);
//		if (ack == 0) {
//			printf("Attenzione, problema irreversibile nel server, client in chiusura\n");
//			go = ack; // basta il break?
//			break; //ok?
//		}
//
//		//è richiesta una conferma da parte dell'utente
//		if(ack < 0) {
//			reply(&res);
//		}



		//eventuale stampa messaggio
		//cosa stampare?
		go = cmd;
		printf("DEBUG go =%d\n", go);
		free(parse_buffer);
		//free dei messaggi
		//parse_buffer = NULL; //perchè non va se non metto questo?
	}

	WRITELN("NMAT Client in uscita..");
	//cleanup!!!!!!!!!!!!!
	return 0;//success
}

void init() {
	EC_M1_E( nmat_pipe = open("clientserver", O_WRONLY), "NMAT Client: errore nell'apertura della pipe con il server!")
	//stringhe per pipe e connessione
	EC_NULL_E(pid = malloc(10 * sizeof(char)), "NMAT Client: malloc error");
	EC_NULL_E(pipe_in = malloc((strlen(pid) + 12) * sizeof(char)), "NMAT Client: malloc error");
	EC_NULL_E(pipe_out = malloc((strlen(pid) + 12) * sizeof(char)), "NMAT Client: malloc error");
	sprintf(pid, "%d", getpid());
	sprintf(pipe_in, "%s%s", "serverclient", pid);
	printf("DEBUG: pipe_in= !%s!\n", pipe_in);
	sprintf(pipe_out, "%s%s", "clientserver", pid);
	printf("DEBUG: pipe_out= !%s!\n", pipe_out);
	//forse meglio tornare al pid come intero?

	EC_NULL_E( login_msg = malloc( (strlen(pid) + 10 + strlen(username)) * sizeof(char)), "NMAT Client: malloc error"); //provare +9
	aux = sprintf(login_msg, "%s%s%s", pid, " CONNECT ", username);
	EC_M1_E( aux,"NMAT Client: ");

	WRITELN("Collegamento al server NMATS in corso...");
	EC_M1_E(write(nmat_pipe, login_msg, aux), "NMAT Client: ");
	printf("DEBUG: dopo write login\n");
	close(nmat_pipe);//da incapsulare in un controllo?
	printf("DEBUG: dopo close nmatpipe\n");
	mkfifo(pipe_in, 0644); //creata prima del login, spostare dopo?
	printf("DEBUG: dopo mkfifo\n");
	printf("DEBUG: pipe_in !%s!\n", pipe_in);
	EC_M1_E( (in = open(pipe_in, O_RDONLY)), "NMAT Client error:" );
	printf("DEBUG: open  pipein= !%d!\n", in);
	EC_NULL_E(buffer = malloc(MAX_CANON * sizeof(char)), "NMAT Client: malloc error");
	printf("DEBUG: prima di read\n");
	EC_M1_E( read(in,buffer,7), "NMAT Client: errore in lettura pipe." );//controllare bene
	printf("DEBUG: prima di read\n");
	printf("DEBUG: buffer = %s\n", buffer);

	switch (strcmp(buffer, "LOGIN 1")) { //ok così o andare a controllare direttamente il codice?
	case 1: {
		WRITELN("Errore, utente già connesso al sistema.");
		exit(0);
	}
	case -1: {
		WRITELN("Errore, nome utente errato.");
		exit(0);
	}
		//aggiungere cleanup?
	}

	WRITELN("Utente, autenticato.");

	/**
	 * Ciclo di apertura pipe dal worker
	 */
	for (con_check = 0; con_check < 5; con_check++) {
		printf("DEBUG ciclo connect #%d\n",con_check);
		//migliorare qui!!!
		out = open(pipe_out, O_WRONLY);
		printf("DEBUG: out = %d\n", out);
		perror("OPEN PIPEOUT!!");
		if (out > 0) {
			WRITELN("NMAT Client: connessione avvenuta!");
			break;
		}
		if (con_check == 4) {
			WRITELN("NMAT Client: Timeout nella comunicazione con il server.");
			exit(EXIT_FAILURE); //cleanup?
		}
		sleep(1);
	}

} //fine init

/**
 * @brief Stampa del menù
 */
void menu() {
	WRITELN("NMATC Debug: inizio menu()!");

	WRITELN(" |  + / - * NMAT Client * - / + | ");
	WRITELN(" |  Menu:                              | ");
	WRITELN(" |  (0)                                  | ");
	WRITELN(" |  (0)                                  | ");
	WRITELN(" |  (0)                                  | ");
	WRITELN(" |  NMAT Client in attesa di comando   | ");

	WRITELN("NMATC Debug: fine menu()!");

}

//fare un controllo sui byte ricevuti ogni volta? per controllare che sia stato ricevuto
//il tipo e la dim giusta?


/**
 * La fuzione parse provvede a effettuare il parsing dell'input dell'utente.
 * -bufferizzazione input
 * -creazione array di token a partire dal buffer
 * -tramite validate si controlla la correttezza dell'input e quale comando è stato impartito
 * -se è necessario inviare un msg viene chiamata la funzione msetup;
 *	-se in uno qualsiasi dei passaggi di parse() viene rilevato un errore, si restituisce -1
 */
// invece dei tanti return usare una var di appoggio e fare return in fondo per unificare le free?

//separare parte di lettura input da parse
//in parse deve rimanere solo tokenizer, cmdcheck
//parse restituisce il codice del comando e ha modificato l'array numbers e parametri
//passo anche indirizzo array token?
int parse(message_t *m) { //ritorna codice comando o tkn num?

	int rd;
	int input_len = 0;
	//char *parse_buffer = NULL; //provare senza  NULL
	//char **tkns; //reso globale
	int debug;
	//char input_buffer[MAX_CANON];//spostato qui

	//FASE DI LETTURA INPUT DA LINEA
	//parse_buffer = malloc(MAX_CANON+1 * sizeof(char));
	printf("INIZIO PARSE\n");
	//cambiare la guardia del while?
	while ((rd = read(0, input_buffer, MAX_CANON)) > 0) { // guardare i +1
		// EC qui??
		//da sistemare il fatto che salti quando legge un solo byte
		printf("DEBUG: LETTI %d BYTES\n", rd);
		input_len += rd;
		if (input_len == 1) { //l'utente ha premuto solo invio
			break;
		}
		if (rd < MAX_CANON) { //non rimane altro da leggere
			input_buffer[rd - 1] = '\0';
		}
		if (parse_buffer == NULL) { //buffer ancora non allocato //forse ANCHe rd== input_len
			//EC_NULL_E(parse_buffer = strdup(input_buffer), "errore");
			EC_NULL_E(parse_buffer = strndup(input_buffer,MAX_CANON), "errore");
			printf("lettura, parsebuffer !%s! len=%d\n",parse_buffer,(int)strlen(parse_buffer));
			//			parse_buffer = malloc(strlen(input_buffer) * sizeof(char));
			//			parse_buffer = strcpy(parse_buffer, input_buffer);
		}
		//controllare errori lettura input quii
		else {
			EC_NULL_E(parse_buffer = (char*)realloc(parse_buffer,input_len * sizeof(char)), "errore");
			strncat(parse_buffer, input_buffer, (size_t) rd); //concatenata l'ultima lettura al buffer
		}
		if (rd < MAX_CANON) { //non rimane altro da leggere
			break;
		}
	} //fine while lettura input
	//FINE FASE DI LETTURA INPUT
	//gestire variabile dimensione
	//o mettere la lunghezza minima del piu comando più corto
	if (input_len <= 1) { //salta solo le stringue vuote
		//segnalare errore?
		return -1;
	}
	printf("PARSE: parsebuffer lungo %d = !%s! passato a tokenizer\n", (int)strlen(parse_buffer),
			parse_buffer);
	//cambiare in modo che mandi un segnale al worker
	//fare che esca in caso di errore malloc...
	//spostare dentro tknzr
	EC_NULL(tkns = malloc(10* sizeof(char*)));//stampare errore?
	//si spezza il buffer in tokens
	tkns_num = tokenizer(parse_buffer, tkns);//controllo errori!!!
	for (debug = 0; debug < tkns_num; debug++) {
		printf("DEBUG DOPO TOKENIZER tokens[%d]=%s\n", debug, tkns[debug]);
	}
	printf("tknsnum = %d\n",tkns_num);
	if (tkns_num < 1) {
		//segnalare errore?
		//free buffer?
		return -1;
	}
	//ho un array di token
	//cmd_check controlla la validità del comando
	cmd_code = cmd_check(tkns_num, tkns);
	//in base al risultato di cmdcheck
	printf("switch cmdcode=%d\n",cmd_code);
	if (cmd_code == -1){ //ritorna -1 in caso di errore
		//ripulire?
		return -1;
	}

	switch (cmd_code) {
	case ERR: {//nessun comando valido,niente da fare
		printf("switch cmdcode errore\n");
		break; //alternativamente usare var di appoggioe  e usarla in un return unico!!
	}

	case _new:{ //bisogna validare i dati inseriti
		//cmd_code = new_check(tkns_num,tkns); //se tutto ok rimane _new, altrimenti -1
		printf("switch new, code ora =%d\n",cmd_code);
		//decidere se chiamare newcheck prima di msetup e nel caso mettere un altro if
	if(new_check(tkns_num,tkns)){
			printf("new check okcode ora =%d\n",cmd_code);
		}
		else{
			cmd_code = -1;
			break;
		}
			//decidere se chiamare newcheck prima di msetup e nel caso mettere un altro if

	
	
	}

	default: {
		printf("switch default\n");
		if (msetup(cmd_code, tkns_num, tkns, &req) == -1) { //int cmd_code, int tnum, char **tkns, message_t *msg
			printf("DEBUG msetup errore\n");
			cmd_code = -1;
			break;
		}
		printf("DEBUG msetup ok\n");
	}

	}//fine switch codice

	printf("FINE PARSE\n");
	return cmd_code; //in ogni caso
	//mettere controllo errori dentro e tra le varie funzioni
	// i token vengono parsati da msetup che valida l'input
	// e restituisce la struct messaggio
}//fine parse





/**
 * @brief
 *
 *  Come prima cosa i vari casi possibili sono suddivisi in base al numero di token
	trovati da tokenizer(). Per ogni numero di token sono possibili solo determinati
	comandi validi.
	Al termine di cmd_check sono validati in modo sicuro tutti i comandi tranne new che necessita
	di un ulteriore controllo sui numeri
 *
 * @param tnum
 * @param tokens
 * @return
 */
int cmd_check(int tnum,char **tokens){
	printf("INIZIO CMD_CHECK\n");
	//provare a ingolbare qui la creazione del msg come in msetup
	switch (tnum) {
	case 1: { //con 1 token exit,display,help
		printf("DEBUG CMDCHECK case 1 = %s\n", tokens[0]);

		if (strcmp(tokens[0], "exit") == 0)
			return _exi;
		if (strcmp(tokens[0], "help") == 0)
			return HELP;
		if (strcmp(tokens[0], "list") == 0)
			return _list;
		else {
			return -1;
		}
	}// fine case 1

	// con 2 token print,save,load o new(cancellazione)
	case 2: {
		printf("DEBUG CMDCHECK case 2\n");
		//controllo preliminare del nome matrice
		if(!name_check(tokens[0]))
			return -1;

		//nome_matrice =
		if ( strcmp(tokens[1], "=") == 0 && name_check(tokens[0]) ){ //cancellazione
			printf("DEBUG CMDCHECK DELETE\n");
			return _del; //codice a parte
		}
		//comandi nella forma: comando nomematrice
		//controllo preliminare del nome matrice
		if(!name_check(tokens[1]))
			return -1;
		//resta da controllare se prima del nome_mat corretto è presente un comando legittimo
		if (strcmp(tokens[0], "print") == 0)
			return _print;
		if (strcmp(tokens[0], "save") == 0)
			return _save;
		if (strcmp(tokens[0], "load") == 0)
			return _load;
		else {
			return -1;
		}
	}// fine case 2

	case 4: { // con 4 token TRASP o nuova matrice con 2 numeri
		printf("DEBUG CMDCHECK case 4\n");
		if ( (strcmp(tokens[1], "=") == 0) && name_check(tokens[0]) ) {//vero in entrambi i casi
			//controllo della conformità del comando TRASP
			if( (strcmp(tokens[2], "TRASP") == 0)  && name_check(tokens[3]) ){
				return _TRASP;
			}
			//l'operatore = è giusto, da controllare conformità parametri numerici.
			return _new;
		}

		return -1;
	}// fine case 4

	case 5: { // con 5 token op o new con 3 elementi
		printf("DEBUG CMDCHECK case 5\n");
		//in tutti i casi deve esserci un nome di matrice
		if(! name_check(tokens[0]) )
			return -1;
		//nome matrice trovato
		if (strcmp(tokens[1], "=") == 0) {
			//controllo che ci siano nomi di matrice
			if ( name_check(tokens[2]) && name_check(tokens[4]) ){ // tutti i casi con k = x op k
				if (strcmp(tokens[3], "+") == 0)
							return _add;
				if (strcmp(tokens[3], "-") == 0)
							return _sub;
				if (strcmp(tokens[3], "*") == 0)
						return _mul;
				return -1;// = OK nomi matrice OK ma nessun comando legittimo

			}
			//nome = x y z
			return _new;
		}
			//non trova = al posto giusto
			return -1;

	}// fine case 5

	default: {
		printf("DEBUG CMDCHECK case default\n");
		if (strcmp(tokens[1], "=") == 0)
			return _new;
		else
			return -1;
	}

	}//fine switch
}


//forse meglio allocare/deallocare prima/dopo new_check
//si passa la lunghezza array token o si fa una sizeof?
/**
 * @brief new_check controlla la validità dei numeri della matrice che si vuole creare
 *
 * Memorizza nella struct messaggio la lunghezza di righe e colonne e l'array di double.
 * @param tkns_num
 * @param tkns
 * @return
 */
int new_check(int tkns_num, char **tkns){ //decidere se passare doulbe* o double**
	int i;
	int terminator;
	int scan_counter = 0;
	int row_len = 0;//colonne quindi
	int rows = 0;
	int n;
	printf("INIZIO NEW_CHECK\n");
	n = tkns_num - 2; //due token sono rispettivamente il nome della matrice e l'operatore =
	//numbers = calloc(n , sizeof(double));//riallocare?
	EC_NULL(req.numbers = calloc(n, sizeof(double))); //meglio una chiamata a funzione di uscita??
	//ciclo di scansione dei token
	for (i = 2; i < tkns_num; i ++){
		printf("ciclo new check\n");
		terminator = num_check(tkns[i], &req.numbers[i]);
		switch(terminator){
		///errore nella conversione del numero
		case -1:{
			printf("NEW CHECK ERRORE CONVERSIONE, uscita da new check return -1\n");
			return -1;
		}
		///nessun terminatore trovato
		case 0:{
			scan_counter++;
			printf("NEW CHECK terminatore NON trovato \n");
			break;
		}

		///trovato un terminatore riga
		case 1:{
			printf("NEW CHECK trovato terminatore\n");
			scan_counter++;

			///primo terminatore
			if(row_len == 0){
				printf("PRIMO terminatore\n");
				row_len = scan_counter;
				scan_counter = 0; //azzerato per le prossime righe
				rows++;
				break;
			}

			///teminatore trovato in posizione errata
			if(scan_counter != row_len) {
				printf("Terminatore in posizione errata\n");
				return -1;
			}

			rows ++;
			printf("Terminatore riga %d colonna %d\n",rows,row_len);
			break;
		}

		}//fine switch

	}// fine for
	
	
	
//fine scansione numeri
	//si ricontrolla terminator che rimane a -1 se sono stati trovati errori

	//in caso di errore liberare la memoria qui?
	if(row_len == 0){
		printf("Errore nell'immissione dati: nessun terminatore di riga\n");
		return -1;
	}
	if(terminator == -1){
			printf("Errore nell'immissione dati: controllare la cifra #%d\n", i-1);
			return -1;
	}
	//schifo
	req.args[1] = strndup(rows ,MAX_INT_STRING);//schifezza
	req.args[2] = strndup(row_len ,MAX_INT_STRING);
	printf("arg1= %s\n",req.args[1]);
	printf("arg2= %s\n",req.args[2]);
	printf("FINE NEW_CHECK OK\n");
	return terminator;
	//decidere per le free
}//fine new_check



int msetup(int cmd_code, int tnum, char **tkns, message_t *msg) { //nella vecchia versione msg era dichiarato in parse() e passato a msetup
	printf("INIZIO MSETUP\n");
	msg->cmdcode = cmd_code;
	msg->args = (char**)malloc(3 * sizeof(char*)); //migliorare
	//controllare se non sia necessario di per se settare a 0 /NULL
	switch (cmd_code) {

	//Formato "comando"
	case (_exi): {
		printf("DEBUG MSETUP: exit\n");
	}
	case _list: {
		printf("DEBUG MSETUP: exit\n");
		msg->nnum = 0;
		msg->numbers = NULL; // unificare?
		msg->argnum = 0;
		msg->args = NULL;
		break;
	}
	//Formato "comando : parametro
	//da implementare controllo validatà parametro
	case _print: {
		printf("DEBUG MSETUP: print\n");
	}

	case _save: {
		printf("DEBUG MSETUP: save\n");

	}

	case _load: {
		printf("DEBUG MSETUP: load\n");
		msg->nnum = 0;
		msg->numbers = NULL;
		msg->argnum = 1;
		msg->args[0] = tkns[1];
		break;
	}

	case _new: {
		printf("DEBUG MSETUP: new\n");
		//new_check
		msg->args[0] = tkns[0];
		break;
	}

	case _TRASP: {
		printf("DEBUG MSETUP: trasp\n");
		msg->argnum= 2;
		msg->args[0] = tkns[0];
		msg->args[1] = tkns[3];
		break;
	}

	case _add: {
		printf("DEBUG MSETUP: add\n");

	}

	case _sub: {
		printf("DEBUG MSETUP: sub\n");

	}

	case _mul: {
		printf("DEBUG MSETUP: mul\n");
		msg->args[0] = tkns[0];
		printf("DEBUG MSETUP OP: assegnamento 1\n");
		msg->args[1] = tkns[2];
		printf("DEBUG MSETUP OP: assegnamento 2\n");
		msg->args[1] = tkns[4];
		printf("DEBUG MSETUP OP: assegnamento 3\n");

		break;

	}

	case _del: {
		printf("DEBUG MSETUP: del\n");
		msg->nnum = 0;
		msg->numbers = NULL;
		msg->argnum = 1;
		msg->args[0] = tkns[0];
		break;
	}

	}//fine switch

	printf("FINE MSETUP\n");

	return 1;
}


//sig handlers da risistemare, printf, etc.
//SIGPIPE


void response(int cmd) {
	WRITELN("response\n");
	msgprint(&res);
	switch(cmd){

	case _load:{

	}

	}
}



void help() {
	WRITELN("heeeeeeeeeeeeeeeeeeeeeelp\n");
}

void pipe_handler(int signum){
	printf("errore sigpipe!!");
	cleanup();
}

void term_handler(int signum) {
	printf("server in chiusura, impossibile effettuare altre operazioni\n");
	cleanup();
	//exit(0);
}

void int_handler(int signum) {
	close(in);
	close(out);
	printf("ricevuto sigint");
	exit(0);
}

void cleanup(void) {
	close(in);
	close(out);
	unlink(pipe_in);
	perror("unlink from server");
	//printf("cleanup");
	exit(0);
}

